﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Threading;

namespace SyncCommon
{
    public enum Method
    {
        GET,
        POST
    }
    public enum FormType
    {
        FormUrlEncode,
        MultiPart
    }
    public class WebRequest
    {
        internal HttpWebRequest innerRequest;
        internal Dictionary<string, string> innerCookies = new Dictionary<string, string>();
        private IContentRender render = null;
        public delegate void OnSendRequestHandler(WebRequest sender, StringBuilder builder);
        public event OnSendRequestHandler OnSendRequest;
        private Dictionary<string, string> extentaionHeader = new Dictionary<string, string>();
        public void RaiseSendRequest(WebRequest sender, StringBuilder builder)
        {
            if (OnSendRequest != null)
            {
                OnSendRequest(sender, builder);
            }
        }

        public Dictionary<string, string> Cookies
        {
            get
            {
                return innerCookies;
            }
            set
            {
                innerCookies = value;
            }
        }

        public Encoding ReturnEncoding
        {
            get;
            set;
        }


        public WebHeaderCollection ResponseHead
        {
            get;
            set;
        }

        public string ResponUri
        {
            get;
            set;
        }

        public string TransferLocation
        {
            get;
            set;
        }

        public bool AllowRedirect
        {
            get
            {
                return innerRequest.AllowAutoRedirect;
            }
            set
            {
                innerRequest.AllowAutoRedirect = value;
            }
        }

        /// <summary>
        /// 默认构造Get请求
        /// </summary>
        /// <param name="url"></param>
        public WebRequest(string url)
        {
            ReturnEncoding = Encoding.UTF8;
            innerRequest = (HttpWebRequest)HttpWebRequest.Create(url);
            SetHeaderValue("Host", new Uri(url).Host);
            innerRequest.Proxy = null;
            innerRequest.KeepAlive = true;
            innerRequest.AllowAutoRedirect = true;
            innerRequest.Timeout = 60000;
            //innerRequest.ContentType = "multipart/form-data; boundary=" + boundary;
            innerRequest.ContentType = "application/x-www-form-urlencoded";
            innerRequest.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1";
        }

        private void ReCreateRequest()
        {

            HttpWebRequest cloneRequest = (HttpWebRequest)HttpWebRequest.Create(innerRequest.RequestUri);
            cloneRequest.Proxy = innerRequest.Proxy;
            cloneRequest.KeepAlive = innerRequest.KeepAlive;
            cloneRequest.AllowAutoRedirect = innerRequest.AllowAutoRedirect;
            cloneRequest.Timeout = innerRequest.Timeout;
            //innerRequest.ContentType = "multipart/form-data; boundary=" + boundary;
            cloneRequest.ContentType = "application/x-www-form-urlencoded";
            cloneRequest.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20100101 Firefox/14.0.1";
            innerRequest = cloneRequest;
            List<string> extKey = new List<string>(extentaionHeader.Keys.Count);
            foreach (string key in extentaionHeader.Keys)
            {
                extKey.Add(key);
            }
            foreach (string key in extKey)
            {
                SetHeaderValue(key, extentaionHeader[key]);
            }
        }

        /// <summary>
        /// 自定义构造
        /// </summary>
        /// <param name="url"></param>
        /// <param name="method"></param>
        /// <param name="type"></param>
        public WebRequest(string url, FormType type, Encoding input, Encoding output)
            : this(url)
        {
            ReturnEncoding = output;
            switch (type)
            {
                case FormType.FormUrlEncode:
                    render = new FormRender(input);
                    break;
                case FormType.MultiPart:
                    render = new MultipartRender(input);
                    break;
                default:
                    break;
            }
        }

        public WebRequest(string url, FormType type, Encoding input)
            : this(url, type, input, Encoding.UTF8)
        {

        }

        public WebRequest(string url, FormType type)
            : this(url, type, Encoding.UTF8, Encoding.UTF8)
        {

        }



        /// <summary>
        /// 用于设置某些微软不开放的
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public void SetHeaderValue(string name, string value)
        {

            var property = typeof(WebHeaderCollection).GetProperty("InnerCollection",
                System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
            if (property != null)
            {
                var collection = property.GetValue(innerRequest.Headers, null) as NameValueCollection;
                collection[name] = value;
                extentaionHeader[name] = value;
            }
        }


        public Stream GetResouce()
        {
            HttpWebResponse response = RetryGet();
            ResponseHead = response.Headers;
            GetResponseCookies(response);
            Stream imageStream = response.GetResponseStream();
            return imageStream;
        }


        public String GetResouceStr()
        {
            HttpWebResponse response = RetryGet();
            GetResponseCookies(response);
            ResponseHead = response.Headers;
            StreamReader resouce = new StreamReader(response.GetResponseStream(), ReturnEncoding);
            string k = resouce.ReadToEnd();
            resouce.Close();
            return k;
        }
        public byte[] GetResouceByte()
        {
            MemoryStream mem = new MemoryStream();
            Stream imageStream = null;
            try
            {
                HttpWebResponse response = RetryGet();
                GetResponseCookies(response);
                ResponseHead = response.Headers;
                imageStream = response.GetResponseStream();
                byte[] buffer = new Byte[512];
                int bytesRead = 0;
                while ((bytesRead = imageStream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    mem.Write(buffer, 0, bytesRead);
                }

                return mem.ToArray();
            }
            catch (Exception exp)
            {
                throw exp;
            }
            finally
            {
                if (imageStream != null)
                {
                    imageStream.Close();
                }

                mem.Close();
            }
        }

        public void AddContent(string key, string value)
        {
            render.AddStrValue(key, value);
        }

        public bool Exist(string key)
        {
            return render.Exist(key);
        }

        public void AddFile(string key, Stream value)
        {
            render.AddFile(key, value);
        }

        public void AddFile(string key, Stream value, string contenttype)
        {
            render.AddFile(key, value, contenttype);
        }

        public void AddFile(string key, byte[] content)
        {
            render.AddFile(key, content);
        }
        public void AddFile(string key, string upload, byte[] content)
        {
            render.AddFile(key, upload, content);
        }

        public void AddFile(string key, string upload, Stream value)
        {
            render.AddFile(key, upload, value);
        }
        /// <summary>
        /// 直接添加内容。谨慎使用
        /// </summary>
        /// <param name="content"></param>
        public void AddPureStr(string content)
        {
            render.AddPureStr(content);
        }

        public string SendWaitString()
        {
            HttpWebResponse response = RetryPostSend();
            ResponseHead = response.Headers;
            GetResponseCookies(response);
            string p = new StreamReader(response.GetResponseStream(), ReturnEncoding).ReadToEnd();
            return p;
        }
        public HttpWebResponse RetryPostSend()
        {
            HttpWebResponse response = null;
            int count = 0;
            while (count < 3)
            {
                try
                {
                    SetRequestCookies();
                    innerRequest.Method = Method.POST.ToString();
                    render.WriteToRequest(this);
                    response = (HttpWebResponse)innerRequest.GetResponse();
                }
                catch (WebException exp)
                {
                    count++;
                    try
                    {
                        innerRequest.Abort();
                    }
                    catch { }
                    Thread.Sleep(1000);
                    ReCreateRequest();
                    continue;
                }
                return response;
            }
            throw new WebException("信息发送失败");
        }
        public HttpWebResponse RetryGet()
        {
            HttpWebResponse response = null;
            int count = 0;
            while (count < 3)
            {
                try
                {
                    SetRequestCookies();
                    innerRequest.Method = Method.GET.ToString();
                    response = (HttpWebResponse)innerRequest.GetResponse();
                }
                catch (WebException exp)
                {
                    count++;
                    try
                    {
                        innerRequest.Abort();
                    }
                    catch { }
                    Thread.Sleep(1000);
                    ReCreateRequest();
                    continue;
                }
                return response;
            }
            throw new WebException("信息发送失败");
        }
        public Stream SendWaitStream()
        {
            HttpWebResponse response = RetryPostSend();
            GetResponseCookies(response);
            ResponseHead = response.Headers;
            return response.GetResponseStream();

        }

        public void SendNoWait(string request)
        {
            HttpWebResponse response = RetryPostSend();
            ResponseHead = response.Headers;
        }

        private void GetResponseCookies(HttpWebResponse response)
        {
            string retCookies = response.Headers["Set-Cookie"];
            if (!string.IsNullOrEmpty(retCookies))
            {
                Regex reg = new Regex("(Domain=.*?(;|,))|(Domain=.*?$)|(Version=.*?(;|,))|(Path=/(;|,))|(Path=/)|(expires=.*?;)|(httponly,)", RegexOptions.IgnoreCase);
                retCookies = reg.Replace(retCookies, "");
                AddStringCookies(retCookies);
            }
            ResponUri = response.ResponseUri.ToString();

            TransferLocation = response.Headers["Location"];
        }


        private void SetRequestCookies()
        {
            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<string, string> keyvalue in innerCookies)
            {
                builder.Append(string.Format("{0}={1}; ", keyvalue.Key, keyvalue.Value));
            }
            innerRequest.Headers.Add("Cookie", builder.ToString());
        }
        public void AddStringCookies(string container)
        {
            string[] con = container.Trim().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
            string key = "";
            string value = "";
            foreach (string k in con)
            {

                string temp = k.Trim();
                int split = temp.IndexOf('=');
                if (split < 0)
                {
                    continue;
                }
                key = temp.Substring(0, split);
                if (split < temp.Length - 1)
                {
                    value = temp.Substring(temp.IndexOf('=') + 1);
                }
                else
                {
                    value = "";
                }
                value = value.Trim('"', ' ');
                innerCookies[key] = value;
            }
        }
    }

    internal interface IContentRender
    {
        void AddStrValue(string key, string value);
        void AddFile(string key, byte[] content);
        void AddFile(string key, string upload, byte[] content);
        void AddFile(string key, string upload, Stream content);
        void AddFile(string key, Stream source, string contenttype);
        void AddFile(string key, Stream source);
        void WriteToRequest(WebRequest request);
        bool Exist(string key);
        /// <summary>
        /// 直接添加内容。
        /// </summary>
        /// <param name="content"></param>
        void AddPureStr(string content);
    }

    internal class FormRender : IContentRender
    {
        private MemoryStream stream = null;
        private StreamWriter writer = null;
        private List<string> Container = new List<string>();
        private Encoding encode;
        public FormRender(Encoding input)
        {
            stream = new MemoryStream();
            encode = input;
            writer = new StreamWriter(stream, Encoding.GetEncoding("GB2312"));
            //writer = new StreamWriter(stream, Encoding.Default);
            //writer.AutoFlush = true;
            //writer.Write(string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode("No1Meaning1", encode), System.Web.HttpUtility.UrlEncode("1", encode)));
        }

        public void AddStrValue(string key, string value)
        {
            Container.Add(key);
            //修改多次。
            if (Container.Count == 1)
            {
                writer.Write(string.Format("{0}={1}", System.Web.HttpUtility.UrlEncode(key, encode), System.Web.HttpUtility.UrlEncode(value, encode)));
            }
            else
            {
                writer.Write(string.Format("&{0}={1}", System.Web.HttpUtility.UrlEncode(key, encode), System.Web.HttpUtility.UrlEncode(value, encode)));
            }
        }

        public void AddPureStr(string content)
        {
            if (Container.Count == 1)
            {
                writer.Write(content);
            }
            else
            {
                writer.Write("&" + content);
            }
        }

        public void AddFile(string key, byte[] content)
        {
            throw new NotImplementedException();
        }

        public void AddFile(string key, string upload, Stream content)
        {

        }

        public void AddFile(string key, Stream source)
        {
            throw new NotImplementedException();
        }
        public void AddFile(string key, Stream content, string contenttype)
        {
            throw new NotImplementedException();
        }

        public void AddFile(string key, string upload, byte[] content)
        {
            throw new NotImplementedException();
        }
        public bool Exist(string key)
        {
            foreach (string p in Container)
            {
                if (key.ToLower().Trim() == p.ToLower().Trim())
                {
                    return true;
                }
            }
            return false;
        }

        private int count = 0;
        public void WriteToRequest(WebRequest request)
        {
            writer.Flush();
            request.innerRequest.ContentType = "application/x-www-form-urlencoded";
            request.innerRequest.ContentLength = stream.Length;
            Stream req = request.innerRequest.GetRequestStream();
            stream.Position = 0;
            stream.WriteTo(req);
        }

    }
    public class MultipartRender : IContentRender
    {
        private MemoryStream stream = null;
        public const string seprate = "--";
        public const string boundary = "---------------------------748270674536";
        private Dictionary<string, string> container = new Dictionary<string, string>();
        private Encoding internalCoding;
        public MultipartRender(Encoding coding)
        {
            internalCoding = coding;
            stream = new MemoryStream();
        }

        public void AddStrValue(string key, string value)
        {
            container.Add(key, value);
        }
        public void AddPureStr(string content)
        {
            byte[] target = this.internalCoding.GetBytes(content);
            stream.Write(target, 0, target.Length);
        }
        public void AddFile(string key, byte[] content)
        {
            StringBuilder prebuilder = new StringBuilder();
            prebuilder.AppendLine(seprate + boundary);
            prebuilder.AppendLine("Content-Disposition: form-data; name=\"FileUpload1\"; filename=\"" + key + "\"");
            prebuilder.AppendLine("Content-Type: " + GetFileType(key));
            prebuilder.AppendLine("");
            Byte[] t = this.internalCoding.GetBytes(prebuilder.ToString());
            stream.Write(t, 0, t.Length);
            stream.Write(content, 0, content.Length);
            byte[] end = this.internalCoding.GetBytes("\r\n");
            stream.Write(end, 0, end.Length);
        }



        public void AddFile(string key, string upload, byte[] content)
        {
            StringBuilder prebuilder = new StringBuilder();
            prebuilder.AppendLine(seprate + boundary);
            prebuilder.AppendLine("Content-Disposition: form-data; name=\"" + upload + "\"; filename=\"" + key + "\"");
            prebuilder.AppendLine("Content-Type: " + GetFileType(key));
            prebuilder.AppendLine("");

            Byte[] t = this.internalCoding.GetBytes(prebuilder.ToString());
            stream.Write(t, 0, t.Length);
            if (content != null)
            {
                stream.Write(content, 0, content.Length);
            }
            byte[] end = this.internalCoding.GetBytes("\r\n");
            stream.Write(end, 0, end.Length);
        }
        public bool Exist(string key)
        {
            if (container.ContainsKey(key))
            {
                return true;
            }
            return false;
        }
        public void AddFile(string key, Stream source)
        {
            StringBuilder prebuilder = new StringBuilder();
            prebuilder.AppendLine(seprate + boundary);
            prebuilder.AppendLine("Content-Disposition: form-data; name=\"FileUpload1\"; filename=\"" + key + "\"");
            prebuilder.AppendLine("Content-Type: " + GetFileType(key));
            prebuilder.AppendLine("");
            Byte[] t = this.internalCoding.GetBytes(prebuilder.ToString());
            stream.Write(t, 0, t.Length);
            byte[] buffer = new Byte[512];
            int bytesRead = 0;
            while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0)
            {
                stream.Write(buffer, 0, bytesRead);
            }
            byte[] end = this.internalCoding.GetBytes("\r\n");
            stream.Write(end, 0, end.Length);
        }

        public void AddFile(string key, Stream source, string ContentType)
        {
            StringBuilder prebuilder = new StringBuilder();
            prebuilder.AppendLine(seprate + boundary);
            prebuilder.AppendLine("Content-Disposition: form-data; name=\"FileUpload1\"; filename=\"" + key + "\"");
            prebuilder.AppendLine("Content-Type: " + ContentType);
            prebuilder.AppendLine("");
            Byte[] t = this.internalCoding.GetBytes(prebuilder.ToString());
            stream.Write(t, 0, t.Length);
            byte[] buffer = new Byte[512];
            int bytesRead = 0;
            while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0)
            {
                stream.Write(buffer, 0, bytesRead);
            }
            byte[] end = this.internalCoding.GetBytes("\r\n");
            stream.Write(end, 0, end.Length);
        }

        public void AddFile(string key, string upload, Stream source)
        {
            StringBuilder prebuilder = new StringBuilder();
            prebuilder.AppendLine(seprate + boundary);
            prebuilder.AppendLine("Content-Disposition: form-data; name=\"" + upload + "\"; filename=\"" + key + "\"");
            prebuilder.AppendLine("Content-Type: " + GetFileType(key));
            prebuilder.AppendLine("");
            Byte[] t = this.internalCoding.GetBytes(prebuilder.ToString());
            stream.Write(t, 0, t.Length);
            byte[] buffer = new Byte[512];
            int bytesRead = 0;
            while ((bytesRead = source.Read(buffer, 0, buffer.Length)) != 0)
            {
                stream.Write(buffer, 0, bytesRead);
            }
            byte[] end = this.internalCoding.GetBytes("\r\n");
            stream.Write(end, 0, end.Length);

        }
        public void WriteToRequest(WebRequest request)
        {
            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<string, string> keyvalue in container)
            {
                builder.AppendLine(seprate + boundary);
                builder.AppendLine("Content-Disposition: form-data; name=\"" + keyvalue.Key + "\"");
                builder.AppendLine("");
                builder.AppendLine(keyvalue.Value);
            }
            request.RaiseSendRequest(request, builder);
            if (builder.Length > 0)
            {
                byte[] buffer = this.internalCoding.GetBytes(builder.ToString());
                stream.Write(buffer, 0, buffer.Length);
            }
            byte[] end = this.internalCoding.GetBytes(seprate + boundary + seprate);
            stream.Write(end, 0, end.Length);

            request.innerRequest.ContentType = "multipart/form-data; boundary=" + boundary;
            request.innerRequest.ContentLength = stream.Length;
            stream.Position = 0;
            Stream req = request.innerRequest.GetRequestStream();
            stream.WriteTo(req);
        }

        private string GetFileType(string name)
        {
            string lowName = name.ToLower();
            if (lowName.EndsWith("png"))
            {
                return "image/png";
            }
            if (lowName.EndsWith("jpg"))
            {
                return "image/jpeg";
            }
            if (lowName.EndsWith("jpeg"))
            {
                return "image/jpeg";
            }
            if (lowName.EndsWith("bmp"))
            {
                return "application/x-bmp";
            }
            return "application/octet-stream";
        }
    }
}
